﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Cache;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;

[assembly : AssemblyDescription("")]
[assembly : AssemblyConfiguration("")]
[assembly : AssemblyCompany("Dmitry Alexandrov aka dimzon")]
[assembly : AssemblyTrademark("")]
[assembly : AssemblyCulture("")]
[assembly : AssemblyVersion("0.8.2.*")]
[assembly : AssemblyTitle("iPictureUploader")]
[assembly : AssemblyProduct("iPictureUploader")]
[assembly : AssemblyCopyright("Copyright © Free Sofware Foundation 2008")]
[assembly : ComVisible(false)]
[assembly : Guid("fd3d0197-7a18-4a21-b614-c3b0b0de31f3")]

public sealed class iPictureUploaderForm : Form
{
	private const string settingsKeyName = @"Software\iPictureUploader";
	private static readonly string[] directFormats = new[] {".jpg", ".jpeg", ".png"};
	private static readonly string[] indirectFormats = new[] {".bmp", ".gif", ".tiff"};
	private static readonly HttpRequestCachePolicy noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
	private static readonly Regex re_images = new Regex(@"(?<=\[IMG\])([^\[]+)(?=\[/IMG\])", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase);
	private static readonly IWebProxy systemWebProxy;
	private static readonly string thisVersion = string.Format("{0} v{1} by {2}", Application.ProductName, Application.ProductVersion, Application.CompanyName);


	private static readonly string[] uploadImage_IMAGEADDICTS_NET_args
		= new[] {"private_upload", "1"};

	private static readonly string[] uploadImage_IMAGEHOST_ORG_args
		= new[] {"a", "upload"};

	private static readonly string[] uploadImage_IMAGESHACK_US_args
		= new[]
		  	{
		  		"MAX_FILE_SIZE", "13145728",
		  		"uploadtype", "on",
		  		"optsize", "320x320",
		  		"refer", string.Empty
		  	};

	private static readonly string[] uploadImage_IPICTURE_RU_args
		= new[]
		  	{
		  		"method", "file",
		  		"ignorAllCheck", "on",
		  		"galleries", "12",
		  		"rotate", "0"
		  	};

	private static readonly string[] uploadImage_PICTABOO_COM_args
		= new[]
		  	{
		  		"myfiletype", "file",
		  		"urldownload", string.Empty,
		  		"tagline", string.Empty,
		  		"dimension", "1600"
		  	};

	private static readonly string[] uploadImage_TINYPIC_COM_args
		= new[]
		  	{
		  		"action", "upload",
		  		"MAX_FILE_SIZE", "200000000",
		  		"dimension", "1600",
		  		"file_type", "image"
		  	};

	private static readonly Encoding windows1251 = Encoding.GetEncoding(1251);

	private readonly List<ToolStripMenuItem> clipboardFormats = new List<ToolStripMenuItem>();
	private readonly List<ToolStripMenuItem> languages = new List<ToolStripMenuItem>();
	private readonly List<ImageUpload> quenie = new List<ImageUpload>();
	private readonly List<ToolStripMenuItem> servers = new List<ToolStripMenuItem>();
	private ToolStripMenuItem clearQuenieToolStripMenuItem;
	private ToolStripMenuItem closeApplicationToolStripMenuItem;

	/// <summary>
	/// Required designer variable.
	/// </summary>
	private IContainer components;

	private ContextMenuStrip contextMenu;
	private ToolStripMenuItem fromClipboardAsJpegToolStripMenuItem;
	private ToolStripMenuItem fromClipboardAsPngToolStripMenuItem;
	private Label mainLabel;
	private ToolStripMenuItem retryToolStripMenuItem;
	private ToolStripMenuItem showVersionInfoToolStripMenuItem;
	private ToolStripSeparator toolStripMenuItem1;
	private ToolStripSeparator toolStripMenuItem2;
	private ToolStripSeparator toolStripMenuItem3;
	private ToolStripSeparator toolStripMenuItem4;
	private ToolStripSeparator toolStripMenuItem5;
	private ToolStripSeparator toolStripMenuItem6;
	private ToolTip toolTip1;
	private ToolStripSeparator toolStripMenuItem7;
	private ToolStripMenuItem useIeProxyToolStripMenuItem;

	static iPictureUploaderForm()
	{
		IWebProxy webProxy = WebRequest.GetSystemWebProxy();
		if (webProxy.Credentials == null)
			webProxy.Credentials = CredentialCache.DefaultNetworkCredentials;
		systemWebProxy = webProxy;
	}
	
	private string t(int index)
	{
		return ((string[])languages[getIndex(languages)].Tag)[index];
	}
	
	private void registerLanguages()
	{
		string[] en = new string[17];
		string[] ru = new string[17];
		en[00] = @"English";
		ru[00] = @"Russian";
		en[01] = @"Drop pictures to be uploaded";
		ru[01] = @"Бросайте сюда файлы для загрузки";
		en[02] = "Upload complete\nTotal: {0}\nUploaded: {1}\nSkipped: {2}";
		ru[02] = "Загрузка завершена\nВсего: {0}\nЗагружено: {1}\nПропущено: {2}";
		en[03] = "Uploading: {3}\nTotal: {0}\nUploaded: {1}\nSkipped: {2}";
		ru[03] = "Загружается: {3}\nВсего: {0}\nЗагружено: {1}\nПропущено: {2}";
		en[04] = @"Direct image link";
		ru[04] = @"Ссылка на изображение";
		en[05] = @"Thumbnail (BBCode)";
		ru[05] = @"Предпросмотр (BBCode)";
		en[06] = @"Embeddable (BBCode)";
		ru[06] = @"Изображение в тексте (BBCode)";
		en[07] = @"Thumbnail (HTML)";
		ru[07] = @"Предпросмотр (HTML)";
		en[08] = @"Embeddable (HTML)";
		ru[08] = @"Изображение в тексте (HTML)";
		en[09] = @"{0} code is in your clipboard now.";
		ru[09] = @"{0} в буфере обмена.";
		en[10] = @"Image code is in your clipboard now";
		ru[10] = @"Данные помещены в буфер обмена";
		en[11] = @"Paste from clipboard as PNG";
		ru[11] = @"Взять из буфера как PNG";
		en[12] = @"Paste from clipboard as JPEG";
		ru[12] = @"Взять из буфера как JPEG";
		en[13] = @"Clear";
		ru[13] = @"Очистить";
		en[14] = @"Attempt to reupload skipped images";
		ru[14] = @"Повторить попытку для пропущенных";
		en[15] = @"Use proxy (settings are obtained from Internet Explorer)";
		ru[15] = @"Использовать прокси (берётся из настроек IE)";
		en[16] = @"Close";
		ru[16] = @"Закрыть";

		int i = 0;
		foreach (string[] language in new string[][]{en,ru})
		{
			ToolStripMenuItem ms = new ToolStripMenuItem();
			ms.Click += switchLanguageMenuItem_Click;
			ms.Text = language[0];
			ms.Tag = language;
			languages.Add(ms);
			int index = contextMenu.Items.IndexOf(toolStripMenuItem7) + languages.Count;
			contextMenu.Items.Insert(index, ms);
			++i;
		}
		
	}

	private void switchLanguageMenuItem_Click(object sender, EventArgs e)
	{
		toggleCheck(languages, (ToolStripItem) sender);
		updateLanguage();
	}

	private void updateLanguage()
	{
		fromClipboardAsPngToolStripMenuItem.Text = t(11);
		fromClipboardAsJpegToolStripMenuItem.Text = t(12);
		clearQuenieToolStripMenuItem.Text = t(13);
		retryToolStripMenuItem.Text = t(14);
		useIeProxyToolStripMenuItem.Text = t(15);
		closeApplicationToolStripMenuItem.Text = t(16);
		int pos = 0;
		foreach (ToolStripMenuItem item in clipboardFormats)
			item.Text = t((++pos) + 3);
		updateUI();
	}

	public iPictureUploaderForm()
	{
		InitializeComponent();

		registerLanguages();
		
		
		
		TopMost = true;
		DoubleClick += MainForm_DoubleClick;
		Load += MainForm_Load;
		Closed += MainForm_Closed;
		mainLabel.DoubleClick += MainForm_DoubleClick;
		mainLabel.TextAlign = ContentAlignment.MiddleCenter;
		mainLabel.ContextMenu = ContextMenu;
		
		useIeProxyToolStripMenuItem.CheckedChanged += UseIeProxyToolStripMenuItem_OnCheckedChanged;
		
		ShowInTaskbar = true;
		Opacity = .70;
		showVersionInfoToolStripMenuItem.Text = thisVersion;

		registerClipboardFormats();
		clipboardFormats[0].Checked = true;

		registerUploaders();
		servers[0].Checked = true;
	}

	private void UseIeProxyToolStripMenuItem_OnCheckedChanged(object sender, EventArgs e)
	{
		lock(lockObject)
		{
			useWebProxy = ((ToolStripMenuItem) sender).Checked;
		}
	}

	private IWebProxy actualWebProxy;
	private bool useWebProxy;
	private UploadDelegate currentEngine;

	/// <summary>
	/// The main entry point for the application.
	/// </summary>
	[STAThread]
	private static void Main()
	{
		Application.EnableVisualStyles();
		Application.SetCompatibleTextRenderingDefault(true);
		Application.Run(new iPictureUploaderForm());
	}

	private static int getIndex(IEnumerable<ToolStripMenuItem> items)
	{
		int i = 0;
		foreach (ToolStripMenuItem item in items)
			if (item.Checked)
				return i;
			else
				++i;
		return 0;
	}

	private static void checkByIndex(IEnumerable<ToolStripMenuItem> items, int itemIndex)
	{
		int i = 0;
		foreach (ToolStripMenuItem item in items)
		{
			item.Checked = (i == itemIndex);
			++i;
		}
	}

	private void loadSettings()
	{
		using (RegistryKey key = getSettingsKey())
		{
			useIeProxyToolStripMenuItem.Checked = 0 != getSettingsValue(key, "P", 0);
			checkByIndex(clipboardFormats, getSettingsValue(key, "F", 0));
			checkByIndex(servers, getSettingsValue(key, "S", 0));
			checkByIndex(languages, getSettingsValue(key, "L", 0));

			int x = getSettingsValue(key, "X", Location.X);
			int y = getSettingsValue(key, "Y", Location.Y);
			Location = new Point(x, y);
			updateLanguage();
		}
	}

	private void saveSettings()
	{
		using (RegistryKey key = getSettingsKey())
		{
			key.SetValue("P", useIeProxyToolStripMenuItem.Checked ? 1 : 0, RegistryValueKind.DWord);
			key.SetValue("S", getIndex(servers), RegistryValueKind.DWord);
			key.SetValue("F", getIndex(clipboardFormats), RegistryValueKind.DWord);
			key.SetValue("L", getIndex(languages), RegistryValueKind.DWord);
			key.SetValue("X", Location.X, RegistryValueKind.DWord);
			key.SetValue("Y", Location.Y, RegistryValueKind.DWord);
		}
	}

	private static int getSettingsValue(RegistryKey key, string subKey, int defaultValue)
	{
		return (int) key.GetValue(subKey, defaultValue);
	}

	private static RegistryKey getSettingsKey()
	{
		return Registry.CurrentUser.OpenSubKey(settingsKeyName, true)
		       ??
		       Registry.CurrentUser.CreateSubKey(settingsKeyName);
	}


	private void MainForm_Load(object sender, EventArgs e)
	{
		loadSettings();
		clearQuenie();
		uploaderThreadReference = new Thread(uploadLoop) { IsBackground = true };
		uploaderThreadReference.Start();
	}

	private void MainForm_Closed(object sender, EventArgs e)
	{
		abortUploaderThread();
		saveSettings();
	}

	private void MainForm_DoubleClick(object sender, EventArgs e)
	{
		lock (lockObject)
		{
			unsafe_UpdateVisibilityOfToClipboardMenuItems();
		}
		foreach (ToolStripMenuItem item in clipboardFormats)
		{
			if (item.Checked && item.Enabled && item.Checked)
			{
				item.PerformClick();
				break;
			}
		}
	}

	private void clearQuenie()
	{
		lock (lockObject)
		{
			quenie.Clear();
			unsafe_UpdateStatsAndUI();
		}
	}

	private void abortUploaderThread()
	{
		if (uploaderThreadReference == null) return;
		uploaderThreadReference.Abort();
		uploaderThreadReference = null;
	}

	private static bool exists(string[] a, string i)
	{
		if(Array.IndexOf(a,i)!=-1)
			return true;
		foreach (string t in a)
			if (string.Equals(t, i))
				return true;
		return false;
	}

	private static bool checkAcceptableFormat(IEnumerable<string> files)
	{
		foreach (string fileName in files)
		{
			string extension = Path.GetExtension(fileName).ToLower();
			if (exists(directFormats, extension) || exists(indirectFormats, extension))
				return true;
		}
		return false;
	}

	private string uploadImage_TINYPIC_COM(ImageUpload upload)
	{
		string refresh = getResponseBody(
			uploadImage(
				upload,
				"http://s3.tinypic.com/upload.php",
				"http://tinypic.com/",
				uploadImage_TINYPIC_COM_args,
				"the_file"), Encoding.ASCII);
		int i = refresh.IndexOf("http://tinypic.com/view.php?pic=");
		if (i <= 0)
			return refresh;

		int j = refresh.IndexOf("\"", i + 1);
		string url = refresh.Substring(i, j - i);

		HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
		prepareRequest(req);
		req.Method = "GET";

		string html = getResponseBody(req.GetResponse(), Encoding.ASCII);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Full = matches[0].ToString();
			upload.Mini = upload.Full.Substring(0, upload.Full.Length - 4) + "_th" + upload.Full.Substring(upload.Full.Length - 4, 4);
		}
		return html;
	}


	private WebResponse uploadImage(ImageUpload upload,
	                                    string requestUriString, string referer,
	                                    string[] args,
	                                    string fileKey)
	{
		string thisBoundary = "---------------------------" + Guid.NewGuid().ToString("n").Substring(1, 14);
		string ext = Path.GetExtension(upload.FileName).ToLower();
		bool mustConvert = false;
		if (exists(indirectFormats, ext))
		{
			ext = ".png";
			mustConvert = true;
		}
		// Спрячем имя файла ;)
		string postfilename = "C:\\" + Guid.NewGuid().ToString("n") + ext;
		string mimeType = Equals(ext, ".png") ? "image/x-png" : "image/pjpeg";
		HttpWebRequest req = (HttpWebRequest) WebRequest.Create(requestUriString);
		prepareRequest(req);
		req.Method = "POST";
		req.Referer = referer;
		req.ContentType = "multipart/form-data; boundary=" + thisBoundary;
		thisBoundary = "--" + thisBoundary;
		const string newline = "\r\n";
		using(Stream requestStream = req.GetRequestStream())
		{
			using (TextWriter tw = new StreamWriter(requestStream, Encoding.ASCII))
			{
				if(args!=null)
					for(int i=0; i<args.Length; i+=2)
					{
						tw.Write("{0}{1}Content-Disposition: form-data; name=\"{2}\"{1}{1}{3}{1}", thisBoundary, newline, args[i], args[i + 1]);
					}
				tw.Write("{0}{1}Content-Disposition: form-data; name=\"{2}\"; filename=\"{3}\"{1}Content-Type: {4}{1}{1}", thisBoundary, newline, fileKey, postfilename, mimeType);		
				tw.Flush();
				writeImage(requestStream, mustConvert, upload);
				tw.Write("{0}{1}--{0}", newline, thisBoundary);
				tw.Flush();
			}
		}
		return req.GetResponse();
	}

	private string uploadImage_IMAGEADDICTS_NET(ImageUpload upload)
	{
		string html = getResponseBody(uploadImage(
		                              	upload,
		                              	"http://www.imageaddicts.net/upload.php",
		                              	"http://www.imageaddicts.net/",
		                              	uploadImage_IMAGEADDICTS_NET_args,
		                              	"userfile[]"), Encoding.UTF8);

		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Mini = matches[0].ToString();
			upload.Full = matches[matches.Count - 1].ToString();
		}
		return html;
	}

	private string uploadImage_IPICTURE_RU(ImageUpload upload)
	{
		HttpWebResponse response;
		string refresh;
		using (response = (HttpWebResponse) uploadImage(
		                                    	upload,
		                                    	"http://ipicture.ru/Upload/",
		                                    	"http://ipicture.ru/",
		                                    	uploadImage_IPICTURE_RU_args,
		                                    	"userfile"))
		{
			//Refresh: 0;url=http://ipicture.ru/Gallery/View/2737564.html
			refresh = response.Headers["Refresh"];
			if (string.IsNullOrEmpty(refresh))
				return getResponseBody(response, windows1251);
		}


		string url = refresh.Substring(6);

		HttpWebRequest req = (HttpWebRequest) WebRequest.Create(url);
		prepareRequest(req);
		req.Method = "GET";

		string html = getResponseBody(req.GetResponse(), windows1251);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Mini = matches[matches.Count - 1].ToString();
			upload.Full = matches[0].ToString();
		}
		return html;
	}

	private string uploadImage_IMAGESHACK_US(ImageUpload upload)
	{
		string html = getResponseBody(
			uploadImage(
				upload,
				"http://imageshack.us/",
				"http://imageshack.us/",
				uploadImage_IMAGESHACK_US_args,
				"fileupload"), Encoding.UTF8);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			foreach (object o in matches)
			{
				string img = o.ToString();
				if (!img.Contains(".th.")) continue;
				upload.Mini = img;
				upload.Full = img.Replace(".th.", ".");
				return html;
			}
			upload.Full = matches[0].ToString();
		}
		return html;
	}


	private static string getResponseBody(WebResponse response, Encoding enc)
	{
		using (response)
		{
			using (Stream responseStream = response.GetResponseStream())
			{
				using (MemoryStream memoryStream = new MemoryStream())
				{
					copyStream(responseStream, memoryStream);
					string html = enc.GetString(memoryStream.ToArray());
					return html;
				}
			}
		}
	}

	private void prepareRequest(HttpWebRequest req)
	{
		req.Proxy = actualWebProxy;
		req.KeepAlive = false;
		req.Pipelined = false;
		req.ProtocolVersion = HttpVersion.Version10;
		req.CachePolicy = noCachePolicy;
		req.Timeout = 5*60*1000;
		req.SendChunked = true;
		req.Accept = "*/*";
		req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
	}
	
	private readonly object lockObject = new object();

	private int total, uploaded, skipped, current;
	private Thread uploaderThreadReference;

	private void uploadLoop()
	{
		for(;;)
		{
			ImageUpload ii;
			UploadDelegate engine;
			lock (lockObject)
			{
				actualWebProxy = useWebProxy ? systemWebProxy : null;
				engine = currentEngine;
				ii = unsafe_UpdateStats(false);
			}
			Invoke(new MethodInvoker(updateUI));
			if (ii == null)
				Thread.Sleep(100);
			else
			{
				try
				{
					// Эти 3 поля модифицируем только мы
					// поэтому здесь лочить не надо
					ImageUpload copy = new ImageUpload {FileName = ii.FileName, Body = ii.Body};
					Trace.WriteLine("uploading: " + copy.FileName);
					
					copy.Html = engine(copy);
					lock (ii)
					{
						ii.Html = copy.Html;
						ii.Full = copy.Full;
						ii.Mini = copy.Mini;
					}
					Trace.WriteLine("uploaded: " + copy.FileName);
					Trace.WriteLine("---------------------8<-----------------------");
					Trace.WriteLine(copy.Html);
					Trace.WriteLine("---------------------8<-----------------------");
				}
				catch (Exception ex)
				{
					Trace.WriteLine("error: " + ii.FileName);
					Trace.WriteLine("---------------------8<-----------------------");
					Trace.WriteLine(ex.ToString());
					Trace.WriteLine("---------------------8<-----------------------");
					lock (ii)
					{
						if (ii.Retry >= ImageUpload.MAX_RETRY)
							ii.Html = ex.ToString();
						else
						{
							++ii.Retry;
							ii.Html = null;
						}
					}
				}
			}
		}
// ReSharper disable FunctionNeverReturns
	}
// ReSharper restore FunctionNeverReturns

	private ImageUpload unsafe_UpdateStats(bool doNotTouchCurrent)
	{
		ImageUpload ii = null;
		int i = 0;
		uploaded = 0;
		skipped = 0;
		if(!doNotTouchCurrent) current = -1;
		total = quenie.Count;
		foreach (ImageUpload imageInfo in quenie)
		{
			++i;
			lock (imageInfo)
			{
				if (imageInfo.Html != null)
				{
					if (imageInfo.Full != null)
						++uploaded;
					else
						++skipped;
					continue;
				}
			}
			if (doNotTouchCurrent || ii != null) continue;
			current = i;
			ii = imageInfo;
		}
		return ii;
	}

	private void updateUI()
	{
		lock (lockObject)
		{
			unsafe_UpdateUI();
		}
	}

	private void unsafe_UpdateUI()
	{
		string currentState;
		if(0 == total)
			currentState = t(01); // "Бросайте сюда файлы для загрузки";
		else
			currentState
				= current == -1
				  	?
				  		string.Format(t(02), //"Загрузка завершена\nВсего: {0}\nЗагружено: {1}\nПропущено: {2}",
				  		              total, uploaded, skipped)
				  	:
				  		string.Format(t(03), //"Загружается: {3}\nВсего: {0}\nЗагружено: {1}\nПропущено: {2}",
				  		              total, uploaded, skipped, current);
			
		mainLabel.Text = currentState;
		toolTip1.SetToolTip(mainLabel, currentState);		
	}

	private void unsafe_UpdateStatsAndUI()
	{
		unsafe_UpdateStats(true);
		unsafe_UpdateUI();
	}

	private void registerUploader(string title, UploadDelegate engine)
	{
		ToolStripMenuItem ms = new ToolStripMenuItem();
		ms.CheckedChanged += uploader_OnCheckedChanged;
		ms.Click += iPictureruToolStripMenuItem_Click;
		ms.Text = title;
		ms.Tag = engine;
		servers.Add(ms);
		int index = contextMenu.Items.IndexOf(toolStripMenuItem1) + servers.Count;
		contextMenu.Items.Insert(index, ms);
	}

	private void uploader_OnCheckedChanged(object sender, EventArgs e)
	{
		lock (lockObject)
		{
			ToolStripMenuItem menuItem = (ToolStripMenuItem)sender;
			currentEngine = (UploadDelegate) menuItem.Tag;
			Trace.WriteLine("engine: " + menuItem.Text);
		}
	}

	private void registerUploaders()
	{
		registerUploader(@"iPicture.ru", uploadImage_IPICTURE_RU);
		registerUploader(@"ImageShack.us", uploadImage_IMAGESHACK_US);
		registerUploader(@"ImageAddicts.net", uploadImage_IMAGEADDICTS_NET);
		registerUploader(@"FreeImageHosting.net", uploadImage_FREEIMAGEHOSTING_NET);
		registerUploader(@"ImageHost.org", uploadImage_IMAGEHOST_ORG);
		registerUploader(@"xs.to", uploadImage_XS_TO);
		registerUploader(@"PictaBoo.com", uploadImage_PICTABOO_COM);
		registerUploader(@"TinyPic.com (JPEG)", uploadImage_TINYPIC_COM);
	}
	
	private void registerClipboardFormats()
	{
		registerClipboardFormat(t(04) /*@"Ссылка на изображение"*/, @"{0} ");
		registerClipboardFormat(t(05) /*@"Предпросмотр (BBCode)"*/, @"[UR" + @"L={0}][I" + @"MG]{1}[/I" + @"MG][/U" + @"RL]");
		registerClipboardFormat(t(06) /*@"Изображение в тексте (BBCode)"*/, @"[IM" + @"G]{0}[/I" + @"MG]");
		registerClipboardFormat(t(07) /*@"Предпросмотр (HTML)"*/, @"<" + @"a hr" + @"ef=""{0}""><im" + @"g src=""{1}"" border=""0""/><" + @"/a>");
		registerClipboardFormat(t(08) /*@"Изображение в тексте (HTML)"*/, @"<im" + @"g src=""{0}"" border=""0""/>");
	}
	
	private void registerClipboardFormat(string title, string formatString)
	{
		ToolStripMenuItem ms = new ToolStripMenuItem();
		ms.Click += placeImageLinksToClipboard_ToolStripMenuItem_Click;
		ms.Text = title;
		ms.Tag = formatString;
		clipboardFormats.Add(ms);
		int index = contextMenu.Items.IndexOf(toolStripMenuItem2) - 1;
		contextMenu.Items.Insert(index, ms);		
	}

	private string uploadImage_PICTABOO_COM(ImageUpload upload)
	{
		string html = getResponseBody(uploadImage(
		                              	upload,
		                              	"http://www.pictaboo.com/cgi-bin/upl.cgi",
		                              	"http://www.pictaboo.com/",
		                              	uploadImage_PICTABOO_COM_args,
		                              	"photo"), Encoding.ASCII);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Full = matches[0].ToString();
			upload.Mini = upload.Full.Replace(".pictaboo.com/f/", ".pictaboo.com/p/");
		}
		return html;
	}

	private string uploadImage_XS_TO(ImageUpload upload)
	{
		string html = getResponseBody(uploadImage(
		                              	upload,
		                              	"http://xs.to/upload.php",
		                              	"http://xs.to/",
		                              	null,
		                              	"thefile"), Encoding.UTF8);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Mini = matches[0].ToString();
			upload.Full = matches[matches.Count - 1].ToString();
		}
		return html;
	}

	private string uploadImage_IMAGEHOST_ORG(ImageUpload upload)
	{
		string html = getResponseBody(uploadImage(
		                              	upload,
		                              	"http://www.imagehost.org/",
		                              	"http://www.imagehost.org/",
		                              	uploadImage_IMAGEHOST_ORG_args,
		                              	"file[]"), Encoding.UTF8);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Mini = matches[0].ToString();
			upload.Full = matches[matches.Count - 1].ToString();
		}

		return html;
	}

	private string uploadImage_FREEIMAGEHOSTING_NET(ImageUpload upload)
	{
		string html = getResponseBody(
			uploadImage(
				upload,
				"http://www.freeimagehosting.net/upload.php",
				"http://www.freeimagehosting.net/upload.php",
				null,
				"attached"), Encoding.UTF8);
		MatchCollection matches = re_images.Matches(html);
		if (matches.Count != 0)
		{
			upload.Mini = matches[0].ToString();
			upload.Full = matches[matches.Count - 1].ToString();
		}
		return html;
	}

	private static void writeImage(Stream requestStream, bool mustConvert, ImageUpload upload)
	{
		if (mustConvert)
			using (MemoryStream memoryStream = new MemoryStream())
			{
				using (Bitmap bitmap = new Bitmap(upload.FileName))
					bitmap.Save(memoryStream, ImageFormat.Png);
				memoryStream.Flush();
				byte[] bytes = memoryStream.ToArray();
				requestStream.Write(bytes, 0, bytes.Length);
			}
		else if (upload.Body == null)
			using (FileStream fs = new FileStream(upload.FileName, FileMode.Open, FileAccess.Read))
				copyStream(fs, requestStream);
		else
			requestStream.Write(upload.Body, 0, upload.Body.Length);
	}

	private static void copyStream(Stream source, Stream target)
	{
		byte[] buff = new byte[4096];

		for (;;)
		{
			int i = source.Read(buff, 0, 4096);
			if (i == 0) break;
			target.Write(buff, 0, i);
		}
	}

	private void MainForm_DragEnter(object sender, DragEventArgs e)
	{
		if (e.Data.GetDataPresent(DataFormats.FileDrop))
		{
			string[] s = (string[]) e.Data.GetData(DataFormats.FileDrop, false);
			if (checkAcceptableFormat(s))
			{
				e.Effect = DragDropEffects.Copy;
				return;
			}
		}
		e.Effect = DragDropEffects.None;
	}

	private void MainForm_DragDrop(object sender, DragEventArgs e)
	{
		if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
		string[] fileNames = (string[]) e.Data.GetData(DataFormats.FileDrop, false);
		List<string> list = new List<string>();
		foreach (string fileName in fileNames)
		{
			string ext = Path.GetExtension(fileName).ToLower();
			if (!exists(directFormats, ext) && !exists(indirectFormats, ext)) continue;
			list.Add(fileName);
		}
		if (list.Count == 0) return;
		list.Sort();
		lock(lockObject)
		{
			foreach (string fileName in list)
				quenie.Add(new ImageUpload {FileName = fileName});
			unsafe_UpdateStatsAndUI();
		}
	}

	private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
	{
		lock (lockObject)
		{
			unsafe_UpdateVisibilityOfToClipboardMenuItems();

			clearQuenieToolStripMenuItem.Visible =
				toolStripMenuItem1.Visible = total != 0;

			retryToolStripMenuItem.Visible = skipped != 0;
		}

		toolStripMenuItem3.Visible =
			fromClipboardAsPngToolStripMenuItem.Visible =
			fromClipboardAsJpegToolStripMenuItem.Visible =
			Clipboard.ContainsImage();
		e.Cancel = false;
	}

	private void unsafe_UpdateVisibilityOfToClipboardMenuItems()
	{
		bool visible = uploaded != 0;
		foreach (ToolStripMenuItem menuItem in clipboardFormats)
			menuItem.Visible = visible;
		toolStripMenuItem2.Visible = visible;
	}

	private static void toggleCheck(IEnumerable<ToolStripMenuItem> items, ToolStripItem sender)
	{
		foreach (ToolStripMenuItem item in items)
			item.Checked = ReferenceEquals(item, sender);
	}

	private void placeImageLinksToClipboard_ToolStripMenuItem_Click(object sender, EventArgs e)
	{
		ToolStripItem item = (ToolStripItem) sender;
		toggleCheck(clipboardFormats, item);
		StringBuilder sb = new StringBuilder();
		lock (lockObject)
		{
			
			foreach (ImageUpload imageInfo in quenie)
				lock(imageInfo)
				{
					if (imageInfo.Full != null)
						sb.AppendFormat((string) item.Tag,
						                imageInfo.Full,
						                imageInfo.Mini ?? imageInfo.Full);
				}
		}
		if (sb.Length == 0) return;
		Clipboard.SetText(sb.ToString().Trim(), TextDataFormat.Text);
		MessageBox.Show(
			this, string.Format(t(09), item.Text ), 
			t(10));
	}

	private void resetQuenieToolStripMenuItem_Click(object sender, EventArgs e)
	{
		clearQuenie();
	}

	private void retryToolStripMenuItem_Click(object sender, EventArgs e)
	{
		lock(lockObject)
		{
			foreach (ImageUpload info in quenie)
			{
				lock(info)
				{
					if (info.Html != null)
						if (info.Full == null)
						{
							info.Html = null;
							info.Retry = 0;
						}
				}
			}
			unsafe_UpdateStatsAndUI();
		}
	}

	private void closeApplicationToolStripMenuItem_Click(object sender, EventArgs e)
	{
		Close();
	}

	private void addImageFromClipboard(bool png)
	{
		if (!Clipboard.ContainsImage()) return;
		using (Image img = Clipboard.GetImage())
		{
			if (img == null) return;
			using (MemoryStream s = new MemoryStream())
			{
				img.Save(s, png ? ImageFormat.Png : ImageFormat.Jpeg);
				string filename = "c:\\cb-" + Guid.NewGuid().ToString("N") + (png ? ".png" : ".jpg");
				lock (lockObject)
				{
					quenie.Add(new ImageUpload {FileName = filename, Body = s.ToArray()});
					unsafe_UpdateStatsAndUI();
				}
			}
		}
	}

	private void imageFromClipboardAsPNGToolStripMenuItem_Click(object sender, EventArgs e)
	{
		addImageFromClipboard(true);
	}

	private void imageFromClipboardAsJPEGToolStripMenuItem_Click(object sender, EventArgs e)
	{
		addImageFromClipboard(false);
	}

	/// <summary>
	/// Clean up any resources being used.
	/// </summary>
	/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
	protected override void Dispose(bool disposing)
	{
		if (disposing && (components != null))
		{
			components.Dispose();
		}
		base.Dispose(disposing);
	}

	private void showVersionToolStripMenuItem_Click(object sender, EventArgs e)
	{
		MessageBox.Show(this, thisVersion, thisVersion);
	}

	private void iPictureruToolStripMenuItem_Click(object sender, EventArgs e)
	{
		toggleCheck(servers, (ToolStripItem) sender);
	}

	#region Windows Form Designer generated code

	/// <summary>
	/// Required method for Designer support - do not modify
	/// the contents of this method with the code editor.
	/// </summary>
	private void InitializeComponent()
	{
		this.components = new System.ComponentModel.Container();
		this.mainLabel = new System.Windows.Forms.Label();
		this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
		this.fromClipboardAsPngToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.fromClipboardAsJpegToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
		this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
		this.clearQuenieToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.retryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
		this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator();
		this.useIeProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator();
		this.closeApplicationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator();
		this.showVersionInfoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
		this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
		this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator();
		this.contextMenu.SuspendLayout();
		this.SuspendLayout();
		// 
		// mainLabel
		// 
		this.mainLabel.Dock = System.Windows.Forms.DockStyle.Fill;
		this.mainLabel.Location = new System.Drawing.Point(0, 0);
		this.mainLabel.Name = "mainLabel";
		this.mainLabel.Size = new System.Drawing.Size(154, 58);
		this.mainLabel.TabIndex = 0;
		this.mainLabel.Text = "mainLabel";
		this.toolTip1.SetToolTip(this.mainLabel, "aaa");
		// 
		// contextMenu
		// 
		this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.fromClipboardAsPngToolStripMenuItem,
            this.fromClipboardAsJpegToolStripMenuItem,
            this.toolStripMenuItem3,
            this.toolStripMenuItem2,
            this.clearQuenieToolStripMenuItem,
            this.retryToolStripMenuItem,
            this.toolStripMenuItem1,
            this.toolStripMenuItem4,
            this.useIeProxyToolStripMenuItem,
            this.toolStripMenuItem6,
            this.closeApplicationToolStripMenuItem,
            this.toolStripMenuItem5,
            this.showVersionInfoToolStripMenuItem,
            this.toolStripMenuItem7});
		this.contextMenu.Name = "contextMenu";
		this.contextMenu.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
		this.contextMenu.ShowCheckMargin = true;
		this.contextMenu.ShowImageMargin = false;
		this.contextMenu.Size = new System.Drawing.Size(313, 200);
		this.contextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening);
		// 
		// fromClipboardAsPngToolStripMenuItem
		// 
		this.fromClipboardAsPngToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.fromClipboardAsPngToolStripMenuItem.Name = "fromClipboardAsPngToolStripMenuItem";
		this.fromClipboardAsPngToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.fromClipboardAsPngToolStripMenuItem.Text = "Взять из буфера как PNG";
		this.fromClipboardAsPngToolStripMenuItem.Click += new System.EventHandler(this.imageFromClipboardAsPNGToolStripMenuItem_Click);
		// 
		// fromClipboardAsJpegToolStripMenuItem
		// 
		this.fromClipboardAsJpegToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.fromClipboardAsJpegToolStripMenuItem.Name = "fromClipboardAsJpegToolStripMenuItem";
		this.fromClipboardAsJpegToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.fromClipboardAsJpegToolStripMenuItem.Text = "Взять из буфера как JPEG";
		this.fromClipboardAsJpegToolStripMenuItem.Click += new System.EventHandler(this.imageFromClipboardAsJPEGToolStripMenuItem_Click);
		// 
		// toolStripMenuItem3
		// 
		this.toolStripMenuItem3.Name = "toolStripMenuItem3";
		this.toolStripMenuItem3.Size = new System.Drawing.Size(309, 6);
		// 
		// toolStripMenuItem2
		// 
		this.toolStripMenuItem2.Name = "toolStripMenuItem2";
		this.toolStripMenuItem2.Size = new System.Drawing.Size(309, 6);
		// 
		// clearQuenieToolStripMenuItem
		// 
		this.clearQuenieToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.clearQuenieToolStripMenuItem.Name = "clearQuenieToolStripMenuItem";
		this.clearQuenieToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.clearQuenieToolStripMenuItem.Text = "Очистить";
		this.clearQuenieToolStripMenuItem.Click += new System.EventHandler(this.resetQuenieToolStripMenuItem_Click);
		// 
		// retryToolStripMenuItem
		// 
		this.retryToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.retryToolStripMenuItem.Name = "retryToolStripMenuItem";
		this.retryToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.retryToolStripMenuItem.Text = "Повторить попытку для пропущенных";
		this.retryToolStripMenuItem.Click += new System.EventHandler(this.retryToolStripMenuItem_Click);
		// 
		// toolStripMenuItem1
		// 
		this.toolStripMenuItem1.Name = "toolStripMenuItem1";
		this.toolStripMenuItem1.Size = new System.Drawing.Size(309, 6);
		// 
		// toolStripMenuItem4
		// 
		this.toolStripMenuItem4.Name = "toolStripMenuItem4";
		this.toolStripMenuItem4.Size = new System.Drawing.Size(309, 6);
		// 
		// useIeProxyToolStripMenuItem
		// 
		this.useIeProxyToolStripMenuItem.CheckOnClick = true;
		this.useIeProxyToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.useIeProxyToolStripMenuItem.Name = "useIeProxyToolStripMenuItem";
		this.useIeProxyToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.useIeProxyToolStripMenuItem.Text = "Использовать прокси (берётся из настроек IE)";
		// 
		// toolStripMenuItem6
		// 
		this.toolStripMenuItem6.Name = "toolStripMenuItem6";
		this.toolStripMenuItem6.Size = new System.Drawing.Size(309, 6);
		// 
		// closeApplicationToolStripMenuItem
		// 
		this.closeApplicationToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.closeApplicationToolStripMenuItem.Name = "closeApplicationToolStripMenuItem";
		this.closeApplicationToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.closeApplicationToolStripMenuItem.Text = "Закрыть";
		this.closeApplicationToolStripMenuItem.Click += new System.EventHandler(this.closeApplicationToolStripMenuItem_Click);
		// 
		// toolStripMenuItem5
		// 
		this.toolStripMenuItem5.Name = "toolStripMenuItem5";
		this.toolStripMenuItem5.Size = new System.Drawing.Size(309, 6);
		// 
		// showVersionInfoToolStripMenuItem
		// 
		this.showVersionInfoToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
		this.showVersionInfoToolStripMenuItem.Name = "showVersionInfoToolStripMenuItem";
		this.showVersionInfoToolStripMenuItem.Size = new System.Drawing.Size(312, 22);
		this.showVersionInfoToolStripMenuItem.Text = "Версия";
		this.showVersionInfoToolStripMenuItem.Click += new System.EventHandler(this.showVersionToolStripMenuItem_Click);
		// 
		// toolStripMenuItem7
		// 
		this.toolStripMenuItem7.Name = "toolStripMenuItem7";
		this.toolStripMenuItem7.Size = new System.Drawing.Size(309, 6);
		// 
		// iPictureUploaderForm
		// 
		this.AllowDrop = true;
		this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 13F);
		this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
		this.ClientSize = new System.Drawing.Size(154, 58);
		this.ContextMenuStrip = this.contextMenu;
		this.Controls.Add(this.mainLabel);
		this.DoubleBuffered = true;
		this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
		this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
		this.Name = "iPictureUploaderForm";
		this.Text = "iPictureUploader";
		this.DragDrop += new System.Windows.Forms.DragEventHandler(this.MainForm_DragDrop);
		this.DragEnter += new System.Windows.Forms.DragEventHandler(this.MainForm_DragEnter);
		this.contextMenu.ResumeLayout(false);
		this.ResumeLayout(false);

	}

	#endregion


	#region Nested type: ImageUpload

	private class ImageUpload
	{
		public const int MAX_RETRY = 32;
		public byte[] Body;
		public string FileName, Html, Full, Mini;
		public int Retry;
	}

	#endregion

	#region Nested type: UploadDelegate

	private delegate string UploadDelegate(ImageUpload imageUpload);

	#endregion
}